/*
 * Copyright (C) ${year} Jeffrey Palm.
 *
 * This code is part of the Floto Project (http://jeffpalm.com/floto/).
 * Please consult the LICENSE included in the distribution.
 */
package com.jeffpalm.floto.isight;

import java.awt.Container;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;

import javax.imageio.ImageIO;

import com.jeffpalm.floto.isight.app.ErrorHandler;
import com.jeffpalm.floto.isight.app.Log;
import com.jeffpalm.floto.isight.app.MessageShower;

abstract class AbstractMain {

  private final WebcamCapture webcamCapture;
  private final MessageShower messageShower;
  private final ErrorHandler errorHandler;
  private final URLRequester urlRequester;
  private final Log log;
  private final ConfigInstance config;

  protected AbstractMain(MessageShower messageShower, ErrorHandler errorHandler, URLRequester urlRequester, Log log,
      Capture.Factory captureFactory) {
    this.messageShower = messageShower;
    this.errorHandler = errorHandler;
    this.urlRequester = urlRequester;
    this.log = log;
    this.webcamCapture = captureFactory.newWebcamCapture();
    config = new ConfigInstance(log, errorHandler);
  }

  protected AbstractMain(Depot depot) {
    this(depot, depot, depot, depot, depot);
  }

  protected AbstractMain() {
    this(Depot.getInstance());
  }

  /**
   * @return the messageShower
   */
  protected final MessageShower getMessageShower() {
    return messageShower;
  }

  /**
   * @return the errorHandler
   */
  protected final ErrorHandler getErrorHandler() {
    return errorHandler;
  }

  /**
   * @return the log
   */
  protected final Log getLog() {
    return log;
  }

  /**
   * @return the webcam capture
   */
  protected final Capture getWebcamCapture() {
    return webcamCapture;
  }

  /**
   * Delegates to {@link ErrorHandler#handle(Throwable, String)}.
   * 
   * @param e
   *          the thrown exception
   * @param msg
   *          message to print
   * @see ErrorHandler#handle(Throwable, String)
   */
  protected void handle(Throwable e, String msg) {
    errorHandler.handle(e, msg);
  }

  /**
   * Delegates to {@link ErrorHandler#handle(Throwable)}.
   * 
   * @param e
   *          the thrown exception
   * @see ErrorHandler#handle(Throwable)
   */
  protected void handle(Throwable e) {
    errorHandler.handle(e);
  }

  /**
   * Returns {@link ConfigInstance#get(String)}
   * 
   * @see ConfigInstance#get(String)
   */
  protected final String getConfig(String key) {
    return config.get(key);
  }

  /**
   * Returns {@link ConfigInstance#get(String,String)}
   * 
   * @see ConfigInstance#get(String,String)
   */
  protected final String getConfig(String key, String defaultValue) {
    return config.get(key, defaultValue);
  }

  /**
   * Delegates to {@link ConfigInstance#set(Srting,String)}.
   * 
   * @see ConfigInstance#set(Srting,String)
   */
  protected final void setConfig(String key, String val) {
    config.set(key, val);
  }

  /**
   * Delegates to {@link Log#note(Object,Object)}.
   * 
   * @param msg
   *          message to note
   * @see Log#note(Object,Object)
   */
  protected final void note(Object msg) {
    log.note(this, msg);
  }

  /**
   * Delegates to {@link MessageShower#showMessage(String)}.
   * 
   * @param msg
   *          message to note
   * @see MessageShower#showMessage(String)
   */
  protected final void showMessage(String msg) {
    messageShower.showMessage(msg);
  }

  /**
   * Delegates to {@link MessageShower#showMessage(Container,String)}.
   * 
   * @param p
   *          {@link Container} in which this goes
   * @param msg
   *          message to note
   * @see MessageShower#showMessage(Container,String)
   */
  protected final int showConfirm(Container p, String msg) {
    return messageShower.showConfirm(p, msg);
  }

  protected final boolean sendPhoto(String url, final BufferedImage image, String imagePrefix) {

    if (image == null) {
      note("Have null image");
      return false;
    }

    note("Image: " + image.getWidth() + "x" + image.getHeight());

    // Send the photo to the user as well as our site
    String outFileName = imagePrefix + System.currentTimeMillis() + ".jpg";

    final File outFile = new File(outFileName);

    // Try to write the image to disk and then send it via email
    // Set this to null to not send the image
    try {
      saveImage(image, outFile);
    } catch (Exception e) {
      handle(e);
      return false;
    }
    try {
      postImage(url, image, outFile);
      outFile.delete();
    } catch (Exception e) {
      handle(e, "Trying to post image " + outFile);
      return false;
    }
    return true;
  }

  private void postImage(String url, BufferedImage image, File outfile) throws Exception {
    Object[] params = new Object[] { "file", outfile };
    note("Sending image to: " + url);
    note(urlRequester.postRequest(url, params));
  }

  /**
   * Saves <code>image</code> to <code>outfileName</code>.
   */
  private void saveImage(BufferedImage image, File outfile) throws Exception {
    ImageIO.write(image, "jpg", new FileOutputStream(outfile));
  }

  private BufferedImage capturePhoto(Capture capture) throws Exception {

    note("Trying to take a " + capture.getName());

    // First try to capture the image
    BufferedImage tmpImage = null;
    try {
      Capture.Snapshot data = capture.takeSnapshot();
      note("Have date " + data);
      if (data != null) {
        tmpImage = data.getImage();
      }
    } catch (Exception e) {
      handle(e);
    }
    return tmpImage;
  }

  /**
   * Captures a snap shot + sends this to our server and sends this image over
   * email if <code>email</code> is not <code>null</null>

   * @returns whether the snap shot was taken
   * @see #captureSnapShotAndSendMessages(String)
   */
  protected final BufferedImage captureWebcamShot() {
    try {
      return capturePhoto(webcamCapture);
    } catch (Exception e) {
      handle(e, "While trying to send webcam picture");
    }
    return null;
  }

  /**
   * @return the config
   */
  protected final ConfigInstance getConfig() {
    return config;
  }
}
